#include "Threads.h"
#include "Server.h"
#include "Dispatcher.h"
#include "Resolver.h"
#include "Logger.h"
#include "WorkQueue.h"
#include "FileSystem.h"
#include "cstdio"
#include <iostream>

void* Threads::AuthenticationThread( void* pServer )
{

	Server& server( *( (Server*)( pServer ) ) );

	while( true )
	{

		while( ! server.LockAuth() );
		pthread_cond_wait( &server.m_AuthReqReady, &server.m_AuthMutex );

		//Rcieved auth Requ , do Athentication 
		Request* pReq	= server.m_ReqAuth.m_pReq;
		pReq->m_bAuth	= false;
		bool tmp		= true;

		server.LOGGER<<"Threads::AuthenticationThread:"<<"Authenticating "<<server.m_ReqAuth.m_pReq->m_strURL<<"\n";

		if( pReq->m_bResolved )
		{
			std::vector<int>::iterator it = 	pReq->m_FileIDs.begin();
			for( ; it < pReq->m_FileIDs.end(); it++  )
			{
				if( !server.fileSystem.AuthenticateFile( *(it), pReq->m_IPAddress  ) )
					{
						tmp = false;
						break;
					}
			}
		}
		else
		{
			if( pReq->m_ReqType == GET )
			{
				//Can't Get a file that wasn't resolved !!
				pReq->m_bAuth = false;
			}
			else//Unresolved PUT
			{
				//New File, only allowed if the file will be created in user directory.
				pReq->m_bAuth  = server.fileSystem.IsUserDirectory( pReq->m_strURL );
			}

		}
		pReq->m_bAuth = tmp;
		server.LOGGER<<"Threads::AuthenticationThread: "<<"File "<<pReq->m_strURL <<" Authentication is: "<<pReq->m_bAuth<<"\n\n";
		server.m_ReqAuth.m_pReq = NULL;
		pthread_cond_signal( &server.m_AuthResReady );
		while( ! server.UnLockAuth() );

	}
	pthread_exit( NULL );

	return NULL;
};
void* Threads::DispatcherThread( void* pServer )
{
	Server& server( *( (Server*)( pServer ) ) );
	server.dispatcher.ParseRequests();
	server.LOGGER<<"Entering: Threads::DispatcherThread\n";

	std::vector< Request* >::iterator it =  server.dispatcher.GetBeginIterator();
	for( ; it < server.dispatcher.GetEndIterator(); it++  )
	{
		while( !server.LockResolveQueue() );
		server.resolver.Enque( *(it)  );
		server.UnLockResolveQueue();
	}
	server.dispatcher.Eraserequests();
	server.LOGGER<<"DispatcherThread Exiting\n";

	pthread_exit(NULL);
	return NULL;
};
void* Threads::ResolverThread( void* pServer )
{
	//TODO: This  function is too long , need t break it down
	Server& server( *( (Server*)( pServer ) ) ) ;
	Request*	pRequest;

	server.LOGGER<<"Entering: Threads::ResolverThread\n";
	//Parse system defined files.
	server.resolver.ParseResolveFile();

	//Create system defined files.
	std::multimap< std::string, int >::iterator it ;
	for( it = server.resolver.m_ResolveMap.begin() ; it != server.resolver.m_ResolveMap.end(); it++  )
	{
		while( !server.LockFileMap());
		server.fileSystem.CreateFile( it->first, it->first, true , it->second  );
		while( !server.UnLockFileMap());
	}

	while( true )
	{
		while( !server.LockResolveQueue() );
		pRequest	= server.resolver.GetReq();
		server.UnLockResolveQueue();

		if( pRequest != NULL )
		{
			server.resolver.Resolve( pRequest );
			server.LockAuth();
			server.Authenticate( pRequest );
			server.UnLockAuth();

			if( pRequest->m_bAuth )
			{
				if( pRequest->m_bResolved )
				{
					if( pRequest->m_ReqType == GET )
					{
						server.LOGGER<<"Threads::ResolverThread: Inserting "<<pRequest->m_strURL<<"\n";
						while( !server.LockWorkQueue() );
						server.workQueue.Insert( pRequest );
						server.UnLockWorkQueue();
						server.LOGGER<<"Threads::ResolverThread "<<pRequest->m_strURL<<" Inserted in work queue successfully \n";
					}
					else//PUT
					{
						//Update All files When resolved put
						while( !server.LockFileMap() );
						server.fileSystem.UpdateFiles( pRequest );
						while( ! server.UnLockFileMap());
						delete pRequest;
					}
				}
				else//File does not exist.
				{
					if( pRequest->m_ReqType == PUT )
					{
						if( pRequest-> FullPathExists() )
						{
							server.LOGGER<<"Threads::ResolverThread"<<"Resolved was a put: "<<pRequest->m_strURL<<"\n";
							server.LOGGER<<"Threads::ResolverThread"<<"Adding new Entry for file"<<pRequest->m_strURL<<"\n";

							//Must call AddEntry first to get value for the New file ID
							server.resolver.AddEntry( pRequest );

							while (!server.LockFileMap() );
							server.fileSystem.CreateFile( pRequest );
							while (!server.UnLockFileMap() );

							while( !server.LockWorkQueue() );
							server.workQueue.Insert( pRequest );
							//server.InsertInWorkQueue( pRequest );
							server.UnLockWorkQueue();

						}
						else
						{
							server.ERRLOGGER<<"Full Path doesn't exist can't create file \n";
							delete pRequest;
						}
					}
					else
					{
						//Can't Get a file that was not resolved !
						server.ERRLOGGER<<"Threads::ResolverThread couldnt resolve "<<pRequest->m_strFileName<<"\n";
						delete pRequest;
					}
				}
			}
			else
			{
				delete pRequest;
				server.LOGGER<<"Threads::ResolverThread AUTHENTICATION FAILURE !!\n";
			}
		}
		else
		{
			server.LOGGER<<"Threads::ResolverThread Resolve Queue is Empty \n";
			sleep( 1 );
		}
	}

	server.LOGGER<<"Leaving: Threads::ResolverThread\n";
	pthread_exit(NULL);
	return NULL;
};
void* Threads::Work( void* pServer )
{
	Server& server( *( (Server*)( pServer ) ) ) ;
	server.LOGGER<<"Threads::Work"<<"starting work\n";

	while(true)
	{

		while( !server.LockWorkQueue()) ;
		if(!server.workQueue.requests.empty())
		{
			server.LOGGER<<"Threads::Work Work queue has values \n";
			Request* pRequest = server.workQueue.requests.front();
			server.workQueue.requests.pop();
			while( !server.UnLockWorkQueue() );
			if(pRequest->m_ReqType == PUT)
			{
				server.fileSystem.HandleStore( pRequest );
			}
			else//GET
			{
				server.fileSystem.HandleRetrv( pRequest );
			}

			delete pRequest;
		}
		else
		{
			server.UnLockWorkQueue();
			server.LOGGER<<"Threads::Work"<<"work Queue Empty\n";
			sleep(2);
		}
	}



	pthread_exit(NULL);
	return NULL;
};
